# pragma once

# include<Arduino.h>
# include<Ticker.h>
# include "../../ganix/ch423.hpp"
# include "../pin_list.hpp"
# include "../io_action_config.hpp"

namespace RMSW
{
    static const int SDA = D2, SCL = D1;
    static const int PIN_INT = D6;
    static const int PIN_SIG_8 = D5;

    uint8_t set_bit(uint8_t x, uint8_t pos, int flag) {
        Serial.printf("set_bit(%x,%d,%d)->", x, pos, flag);
        uint8_t mask = 1 << pos;
        if (flag == 1) {
            x |= mask;
        } else if (flag == 0) {
            x &= (~mask);
        } else if (flag == -1) {
            x ^= mask;
        }
        Serial.printf("%x\n", x);
        return x;
    }

    class Ch423 {
        private:
        uint8_t io;
        uint8_t oc_high, oc_low;
        public:
        void init() {
            CH423_Init(SDA, SCL, false, false);
            io = CH423_ReadByte();
            CH423_WriteOC(oc_high = 0, oc_low = 0);
            pinMode(PIN_INT, INPUT_PULLUP);
        }

        bool doublecheck(uint8_t x) {
            for (int i=0;i<3; i++) {
                delay(10);
                int latest = CH423_ReadByte();
                if (latest != x) {
                    Serial.println("doublecheck failed.");
                    return false;
                }
            }
            return true;
        }

        void loop() {
            int cur = CH423_ReadByte();
            if (cur != io && doublecheck(cur)) {
                int last = io;
                io = cur;
                Serial.printf("ch423 read :%x\n", io);
                for (int i=0; i<8; i++) {
                    int msk = 1<<i;
                    int x = last & msk;
                    int y = io & msk;
                    if (x!=y) {
                        String name = "CH423-" + String(i);
                        trigger_input_pin(name, y != 0);
                    }
                }
            }
        }

        void set_oc(uint8_t pin, int flag) {
            if (pin < 8) {
                Serial.println("sch423 et oc low");
                oc_low = set_bit(oc_low, pin, flag);
            } else {
                Serial.println("sch423 et oc high");
                oc_high = set_bit(oc_high, pin - 8, flag);
            }
            Serial.printf("ch423 setting oc(high = %x, low = %x)\n", oc_high, oc_low);
            CH423_WriteOC(oc_high, oc_low);
        }

        int get_io() {
            return io;
        }
    }ch423;

    class Ch423OutputPin: public AbstructOutputPin {
        int pin_num;
        int state;
        public:
        Ch423OutputPin(int _pin_num): pin_num(_pin_num) {
            name = "ch423-oc" + String(_pin_num);
        }

        void set_state(int sw) {
            if (sw == -1) {
                state = 1 - state;
            } else {
                state = sw;
            }
            Serial.printf("changing ch423 state. (%d, %d)\n", pin_num, state);
            ch423.set_oc(pin_num, state);
        }

        int get_state() {
            return state;
        }

        void set_pwm(float pwm) { } // not support
    }sw0(11), sw1(10), sw2(9), sw3(8);

    
    void tick_sig_8() {
        static int sig_8_state = digitalRead(PIN_SIG_8);
        int x = digitalRead(PIN_SIG_8);
        if (x != sig_8_state) {
            Serial.println("sig8_change!");
            sig_8_state = x;
            trigger_input_pin("PIN_SIG_8", sig_8_state);
        }
    }

    Ticker remote_sw_loop_ticker;

    void rmsw_loop() {
        //Serial.println("remote sw loop.");
        ch423.loop();
        tick_sig_8();
    }

    void init() {
        if (!WORKER_IS_RMSW) return;
        Serial.println("remote sw init start.");
        ch423.init();

        output_pin_list.push_back(static_cast<AbstructOutputPin*> (&sw0));
        output_pin_list.push_back(static_cast<AbstructOutputPin*> (&sw1));
        output_pin_list.push_back(static_cast<AbstructOutputPin*> (&sw2));
        output_pin_list.push_back(static_cast<AbstructOutputPin*> (&sw3));

        pinMode(PIN_SIG_8, INPUT);

        remote_sw_loop_ticker.attach_ms_scheduled(50, rmsw_loop);
        Serial.println(WORKER_NAME + " inited. In remote_sw.hpp.");
    }
} // namespace RMSW
